<?php

namespace app\common\controller;

use think\Config;
use think\Hook;
use think\Db;
use think\Request;
use app\api\library\UnauthorizedException;
use app\api\library\Err;
use book\Util;
use think\Env;
use app\common\library\Redis;

/**
 * API控制器基类
 */
class ApiController extends Api
{
    const CACHE_KEY = 'sysconfig:';

    protected $corsheaders = [];

    protected $tableName = null;
    protected $fields = true;
    protected $fieldsExcept = false;
    protected $onlySelf = true;
    protected $conditions = [];
    protected $orderBy = false;
    protected $paramFilter = '';
    protected $queryFields = false;
    protected $allowUserStatus = [];
    protected $join = [];
    protected $redis = null;


    /**
     * 操作成功返回的数据
     * @param string $msg    提示信息
     * @param mixed  $data   要返回的数据
     * @param int    $code   错误码，默认为1
     * @param string $type   输出类型
     * @param array  $header 发送的 Header 信息
     */
    protected function success($msg = '', $data = null, $code = 200, $type = null, array $header = [])
    {
        parent::success($msg, $data, $code, $type, $this->corsheaders);
        // return $this->res($data, $code);
    }

    /**
     * 操作失败返回的数据
     * @param string $msg    提示信息
     * @param mixed  $data   要返回的数据
     * @param int    $code   错误码，默认为0
     * @param string $type   输出类型
     * @param array  $header 发送的 Header 信息
     */
    protected function error($msg = '', $data = null, $code = 200, $type = null, array $header = [])
    {
        return $this->fail($msg, $code, Err::COMMON_ERR);
    }

    protected function res($data = null, $code = 200)
    {
        if (null === $data) {
            $response = \think\Response::create(null, 'json', 204)->header($this->corsheaders);
            throw new \think\exception\HttpResponseException($response);
        }
        return json($data, $code, $this->corsheaders);
    }

    protected function fail($error, $code = 200, $errorCode = Err::COMMON_ERR, $errorVars = [])
    {
        $result = [
            'code' => $errorCode,
            'msg' => __($error, $errorVars),
            'time' => \think\Request::instance()->server('REQUEST_TIME'),
        ];

        $response = \think\Response::create($result, 'json', $code)->header($this->corsheaders);
        throw new \think\exception\HttpResponseException($response);
    }

    protected function invalidParam($name = [])
    {
        if (empty($name)) {
            $this->fail('Invalid parameters', 422, Err::INVALID_PARAM);
        } else {
            if (is_array($name)) {
                $name = implode(",", $name);
            } else {
                $name = [$name];
            }

            $this->fail('Invalid parameter: %s', 422, Err::INVALID_PARAM, $name);
        }
    }

    protected function validateFailed($error)
    {
        if (is_string($error)) {
            $this->fail($error, 422, Err::INVALID_PARAM);
        } else if (is_array($error)) {
            $msg = implode('\n', array_values($error));
            $this->fail($msg, 422, Err::INVALID_PARAM);
        } else {
            $this->fail('Invalid parameters', 422, Err::INVALID_PARAM);
        }
    }

    protected function buildQuery()
    {
        if (!$this->tableName) {
            exception('tableName is missing');
        }

        $query = Db::name($this->tableName);
        if ($this->onlySelf) {
            $userId = $this->auth->getUser()->id;
            if (!$userId) {
                exception('userId is missing');
            }
            $this->conditions['user_id'] = $userId;
        }
        $query = $query->alias('a');
        if ($this->join) {
            $query = $query->join($this->join);
        }
        $query = $query->where($this->conditions);

        list($start, $limit) = $this->paging();

        if ($this->queryFields && is_array($this->queryFields)) {
            $q = $this->request->get();
            if (isset($q['where'])) {
                $q = json_decode(htmlspecialchars_decode($q['where']), true);
                if (!$q || !is_array($q)) {
                    $this->invalidParam('where');
                }
            }
            $q = Util::filterByKey($q, $this->queryFields);
            if (!empty($q)) {
                $query = $query->where($q);
            }
        }

        if (true !== $this->fields) {
            $query = $query->field($this->fields, $this->fieldsExcept);
        }

        if (false !== $this->orderBy) {
            $query = $query->order($this->orderBy);
        }

        $query = $query->limit($start, $limit);
        return $query;
    }

    protected function processData($method, $data)
    {
        return $data;
    }

    protected function getLimit()
    {
        $max = config('cashbell.max_limit');
        $limit = $this->request->get('limit', $max);
        if ($limit > $max) {
            $this->fail('Limit cannot exceed ' . $max, 422);
        }
        return $limit;
    }

    /**
     * 显示资源列表
     *
     * @return \think\Response
     */
    public function index()
    {
        $params = $this->request->param('', null, $this->paramFilter);
        try {
            $this->processData('beforeList', $params);
            $data = $this->buildQuery()->select();
            $data = $this->processData('afterList', $data);
        } catch (\think\exception\ErrorException $e) {
            $msg = $e->getMessage();
            $file = $e->getFile();
            $line = $e->getLine();
            trace("msg: $msg | file: $file | line: $line", 'error');
            $this->fail('Failed find data');
        }

        return $this->success('', $data);
    }

    /**
     * 显示指定的资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function read($id)
    {
        $data = $this->buildQuery()->find($id);
        if (!$data) {
            $this->fail('No data is avaliable', 404);
        }
        return $this->res($data);
    }

    /**
     * 保存新建的资源
     *
     * @param  \think\Request  $request
     * @return \think\Response
     */
    public function save(Request $request)
    {
        $data = $this->request->param('', null, $this->paramFilter);
        Db::startTrans();

        try {
            $data = $this->processData('beforeSave', $data);
        } catch (\think\exception\ErrorException $e) {
            $msg = $e->getMessage();
            $file = $e->getFile();
            $line = $e->getLine();
            trace("msg: $msg | file: $file | line: $line", 'error');
            Db::rollback();
            $this->fail(__('Failed to update data'));
        }

        if ($this->onlySelf) {
            $user = $this->auth->getUser();
            $data['user_id'] = $user->id;
        }

        try {
            $id = Db::name($this->tableName)->insertGetId($data);
            if (!$id) {
                $this->fail(__('Failed to update data'));
            }
            $data['id'] = $id;
            $data = $this->processData('afterSave', $data);
            Db::commit();
        } catch (\think\exception\ErrorException $e) {
            $msg = $e->getMessage();
            $file = $e->getFile();
            $line = $e->getLine();
            Db::rollback();
            trace("msg: $msg | file: $file | line: $line", 'error');
            $this->fail(__('Failed to update data'));
        } catch (\think\Exception $e) {
            $msg = $e->getMessage();
            trace("msg: $msg", 'error');
            Db::rollback();
            $this->fail(__('Failed to update data'));
        }

        return $this->success('', $data);
    }

    /**
     * 显示编辑资源表单页.
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * 保存更新的资源
     *
     * @param  \think\Request  $request
     * @param  int  $id
     * @return \think\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * 删除指定资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function delete($id)
    {
        //
    }

    protected function getConfig($name, $key, $default = false)
    {
        return \Cache::tag($name)->remember(self::CACHE_KEY . "$name:$key", function () use ($name, $key, $default) {
            return \Db::name('config')->where(['name' => $name, 'key' => $key])->value('value') ?: $default;
        });
    }

    protected function uploadFile($file, $savePath = "tmp/")
    {
        $path = 'upload/' . $savePath;
        $info = $file->move('upload/' . $path);
        if ($info) {
            return 'http://' . $_SERVER['SERVER_NAME'] . '/' . $path . str_replace('\\', '/', $info->getSaveName());
        } else {
            exception('Failed to upload file:' . $file->getError());
        }

        // $this->request->file()
        // $setting = config('UPLOAD');
        // $config = config("UPLOAD_CONFIG");

        // if (empty($setting) || empty($config)) {
        //     exception("UPLOAD setting missing");
        // }

        // $driver = $config["driver"] ?: "Local";

        // $setting["savePath"] = $savePath ?: "Temp";
        // $setting["rootPath"] = $config["rootPath"] ?: "./Uploads";

        // $downloadRoot = $config["downloadRoot"];
        // if (empty($downloadRoot)) {
        //     exception("downloadRoot setting missing");
        // }

        // $uploader = new \Think\Upload($setting, $driver, $config);
        // $info = $uploader->upload($_FILES);
        // if ($info && $info['file']) {
        //     return [
        //         "res" => true,
        //         "url" => $downloadRoot . $info['file']["savepath"] . $info['file']["savename"]
        //     ];
        // }
        // return [
        //     "res" => false,
        //     "err" => $uploader->getError()
        // ];
    }

    protected function allowUserStatus()
    {
        if (empty($this->allowUserStatus)) {
            return true;
        }
        if (!in_array($this->auth->status, $this->allowUserStatus)) {
            throw new UnauthorizedException(__('You have no permission'), 403);
        }
    }


    /**
     * 静态资源host
     */
    protected static function staticHost()
    {
        $host = 'http://' . $_SERVER['SERVER_NAME'];
        $hooks = config('addons.hooks');
        $alioss = isset($hooks['upload_config_init']) && $hooks['upload_config_init'][0] == "alioss" ? true : false;
        $config = get_addon_config('alioss');

        return $alioss ? $config['cdnurl'] : $host;
    }

    protected function imgAddHost($imgs)
    {
        if (!$imgs) {
            return $imgs;
        }
        $imgs = explode(',', $imgs);
        $arr = [];
        $preg = "/^http(s)?:\\/\\/.+/";
        foreach ($imgs as $img) {
            array_push($arr, preg_match($preg, $img) ? $img : self::staticHost() . $img);
        }
        return implode(',', $arr);
    }

    /**
     * 分页
     */
    protected function paging()
    {
        $page = $this->request->param('page', 1);
        $pageSize = $this->request->param('pageSize', 10);
        $pageSize = $pageSize > 100 ? 100 : $pageSize;
        $offset = ($page - 1) * $pageSize;
        return [$offset, $pageSize];
    }

    protected function getRedis()
    {
        if (!$this->redis) {
            $config = [
                'host' => Env::get('redis.host', '127.0.0.1'),
                'port' => Env::get('redis.port', 6379),
                'password' => Env::get('redis.password', ''),
                'select' => Env::get('redis.select', 0) + 1,
                'timeout' => 0,
                'expire' => 0,
                'persistent' => false,
                'prefix' => '',
            ];

            $this->redis = new Redis($config);
        }
        return $this->redis;
    }
}
